//============================================================================
// Name        : region.cpp
// Author      : dfoelber
//============================================================================

// INCLUDES //
#include "region.h"
#include "effect/erase_effect.h"
#include "effect/slide_effect.h"
#include "render_manager.h"
#include "image_manager.h"
#include <iostream>
//////////////

using namespace std;

/**
* Constructor
**/
Region::Region()
{
	active = NULL;
	hoverObject = NULL;
	dirty = false;
	visible = true;
	aiCommand = 0;
}

/**
* Destructor
**/
Region::~Region()
{
	for( int i = 0; i < (int)objects.size(); i++ )
	{
		delete objects[i];
	}
}

/**
* Sets the region's boundary.
* 
* @param x The boundary's X coordinate
* @param y The boundary's Y coordinate
* @param width The boundary's width
* @param height The boundary's height
**/
void Region::SetBoundary( int x, int y, int width, int height )
{
	boundary.SetBoundary( x, y, width, height );
}

/**
* Checks if the region is dirty and requires a total update.
* 
* @return If the region is dirty
**/
bool Region::IsDirty()
{
	return dirty;
}

/**
* Sets the region to be dirty and requires a total update.
* 
* @param dirty If the region is dirty
**/
void Region::SetDirty( bool dirty )
{
	this->dirty = dirty;
}

/**
* Checks if the region is visible.
* 
* @return If the region is visible
**/
bool Region::IsVisible()
{
	return visible;
}

/**
* Sets the region's visibility.
* 
* @param visible If the region is visible
**/
void Region::SetVisible( bool visible )
{
	if( this->visible != visible )
		dirty = true;
	this->visible = visible;
}

/**
* Add an object, the region becomes the owner.
* 
* @param object The object to add
**/
void Region::AddObject( Object* object )
{
	objects.push_back( object );
}

/**
* Gets the active object.
* 
* @return The active object
**/
Object* Region::GetActiveObject()
{
	return active;
}

/**
* Sets the active object.
* 
* @param object The new active object
**/
void Region::SetActiveObject( Object* object )
{
	active = object;
}

/**
* Gets the object in the object list.
* 
* @param index The index in the object list
* @return The object in the specific position in the list
**/
Object* Region::GetObject( int index )
{
	return objects[ index ];
}

/**
* Notify the region of an event.
* 
* @param event The event that happened
* @return If the region used the event
**/
bool Region::ProcessEvent( SDL_Event event )
{
	if( active != NULL )
	{
		// Allow the active object to possibly use the input.
		if( active->GetPath()->Input( event ) )
		{
			return true;
		}
	}
	
	if( event.type == SDL_MOUSEMOTION )
	{
		mouseX = event.button.x;
		mouseY = event.button.y;
		
		CheckForHover();
	}
	
	return false;
}

/**
* Notifies of time passing.
* 
* @param The amount of time, in milliseconds, that passed since the last call
**/
void Region::TimePassed( int ms )
{
#ifdef REGION_DEBUG
	int debugTicks = SDL_GetTicks();
#endif
	
	// OK, alot happens here.
	// Active object moves first
	if( GetActiveObject() )
	{
		// First, the object is moved via its path
		GetActiveObject()->TimePassed( ms );
		
		// If the object accepts collisions and is moving
		if( !GetActiveObject()->IgnoresCollisions() && GetActiveObject()->GetPath()->IsMoving() )
		{
			// Next, the object is checked against the boundary
			boundary.Check( GetActiveObject()->GetPath() );
			
			// Then, collision with other objects
			for( int o = 0; o < (int)objects.size(); o++ )
			{
				// If the other object ignores collisions, continue
				if( objects[o]->IgnoresCollisions() )
					continue;
				
				if( objects[o] != GetActiveObject() )
				{
					GetActiveObject()->GetPath()->CheckCollision( objects[o]->GetPath() );
				}
			}
		}
	}
	
#ifdef REGION_DEBUG
	cout << "[Region Timepassed] Active Object Time Passed - " << (SDL_GetTicks() - debugTicks) << endl; 
	debugTicks = SDL_GetTicks();
#endif
	
	// Next, the other objects move.
	for( int i = 0; i < (int)objects.size(); i++ )
	{
		if( objects[i] == GetActiveObject() ) 
			continue;
			
		// First, the object is moved via its path
		objects[i]->TimePassed( ms );
		
		// If the object accepts collisions and is moving
		if( !objects[i]->IgnoresCollisions() && objects[i]->GetPath()->IsMoving() )
		{
			// Next, the object is checked against the boundary
			boundary.Check( objects[i]->GetPath() );
			
			// Then, collision with other objects
			for( int o = 0; o < (int)objects.size(); o++ )
			{
				if( objects[o] != objects[i] )
				{
					// If the other object ignores collisions, continue
					if( objects[o]->IgnoresCollisions() )
						continue;
					
					objects[i]->GetPath()->CheckCollision( objects[o]->GetPath() );
				}
			}
		}
	}

#ifdef REGION_DEBUG
	cout << "[Region Timepassed] Other Objects Time Passed - " << (SDL_GetTicks() - debugTicks) << endl; 
	debugTicks = SDL_GetTicks();
#endif
	
	// Check all the collision lists and do a check if found.
	// (since most of these vectors are small, it isn't as bad as it looks)
	for( int i = 0; i < (int)objects.size(); i++ ) // the moving objects
	{
		if( objects[i]->GetPath()->IsMoving() )
		{
			for( int o = 0; o < (int)objects.size(); o++ ) // the stationary object
			{
				if( objects[o] != objects[i] )
				{
					for( int c = 0; c < (int)objects[o]->GetCollisionTypes().size(); c++ )
					{
						if( objects[o]->GetCollisionTypes()[c] == objects[i]->GetID() &&
							ShouldCheckCollisionList(objects[i], objects[o]) )
						{
							if( objects[i]->GetPath()->CheckCollision( objects[o]->GetPath() ) )
							{
								CollisionListHit(objects[i], objects[o]);
							}
						}
					}
				}
			}
		}
	}
	
#ifdef REGION_DEBUG
	cout << "[Region Timepassed] Collision Hit List - " << (SDL_GetTicks() - debugTicks) << endl; 
	debugTicks = SDL_GetTicks();
#endif	
	
	// Next advance all objects and update the dirty rectangles
	for( int i = 0; i < (int)objects.size(); i++ )
	{
		objects[i]->GetPath()->Advance();
	}
}

/**
* A chance for a region to add a custom rendering request. Anything in the object list will
* render automatically.
**/
void Region::Render()
{
}

/**
* Gets the list of objects in the region.
* 
* Overridden from RegionEffectOwner.
* 
* @region Objects currently in the region
**/
vector<Object*>& Region::GetObjects()
{
	return objects;
}

/**
* Checks if the object passed is the active object or in the active object's association list.
* 
* Overridden from RegionEffectOwner.
* 
* @param object The object to check
* @return If the object is the active object or in the active object's association list
**/
bool Region::IsObjectActiveOrSimilar(Object* object)
{
	if( object == GetActiveObject() )
		return true;
	
	for( int i = 0; i < (int)GetActiveObject()->GetAssociations().size(); i++ )
	{
		if( object == GetActiveObject()->GetAssociations()[i] )
			return true;
	}
	
	return false;
}

/**
* Removes the given object from the region.
* 
* Overridden from RegionEffectOwner.
* 
* @param object The object to remove
* @param del If the object should be deleted
**/
void Region::RemoveObject( Object* object, bool del )
{
	for( int i = 0; i < (int)objects.size(); i++ )
	{
		if( object == objects[i] )
		{
			RenderManager::GetManager()->RemoveObject(object);
			
			objects.erase( objects.begin() + i );
		
			if( del )
			{
				delete object;
				object = NULL;
			}
			
			CheckForHover();
			
			return;
		}
	}
}

/**
* Gets the object, if any, that correspond to the X,Y coordinates.
* 
* Overridden from RegionEffectOwner.
* 
* @param x The X coordinate
* @param y The Y coordinate
* @return The associated object, or null if none correspond
**/
Object* Region::GetObjectByCoordinate(int x, int y)
{
	for( int i = 0; i < (int)objects.size(); i++ )
	{
		if( x >= objects[i]->GetPath()->GetX() && 
			x <= objects[i]->GetPath()->GetX() + objects[i]->GetPath()->GetWidth() &&
			y >= objects[i]->GetPath()->GetY() && 
			y <= objects[i]->GetPath()->GetY() + objects[i]->GetPath()->GetHeight() )
		{
			return objects[i];
		}
	}
	
	return NULL;
}

/**
* Checks for a hoverring object.
**/
void Region::CheckForHover()
{
	Object* current = GetObjectByCoordinate( mouseX, mouseY );
	if( hoverObject != NULL && (current != hoverObject || 
								mouseX < hoverObject->GetPath()->GetX() ||
								mouseX > hoverObject->GetPath()->GetX() + hoverObject->GetPath()->GetWidth() ||
								mouseY < hoverObject->GetPath()->GetY() ||
								mouseY > hoverObject->GetPath()->GetY() + hoverObject->GetPath()->GetHeight()) )
	{
		hoverObject->SetHover( false );
	}
	hoverObject = current;
	if( hoverObject != NULL )
	{
		hoverObject->SetHover( true );
	}
}

TooltipInfo* Region::GetTooltipInfo()
{
	for( int i = 0; i < (int)objects.size(); i++ )
	{
		if( GetMouseX() >= objects[i]->GetPath()->GetX() &&
				GetMouseX() <= objects[i]->GetPath()->GetX() + objects[i]->GetPath()->GetWidth() &&
				GetMouseY() >= objects[i]->GetPath()->GetY() &&
				GetMouseY() <= objects[i]->GetPath()->GetY() + objects[i]->GetPath()->GetHeight() )
		{
			return objects[i]->GetTooltip();
		}
	}
	
	return NULL;
}
